#include <unistd.h>
#include <stdlib.h>

#include "dllist.h"

int
DLL_IsEmpty(PDLLIST pdll)
{
  return pdll->pHeadDLLElem==NULL;
}

void
DLL_Init(PDLLIST pdll)
{
  pdll->pHeadDLLElem=NULL;
  pdll->pTailDLLElem=NULL;
}

void
DLL_Kill(PDLLIST pdll)
{
  while(pdll->pHeadDLLElem)
  {
    DLL_RemoveHead(pdll);
  }
}

static PDLLELEM
CreateElement(void *pvData, PDLLELEM pPrev, PDLLELEM pNext)
{
  PDLLELEM pNewDLLElem;

  /* Alloc mem for new element */
  pNewDLLElem=malloc(sizeof(DLLELEM));
  if(!pNewDLLElem)
  {
    return NULL;
  }

  /* Fill new element members*/
  pNewDLLElem->pvData=pvData;
  pNewDLLElem->pPrevDLLElem=pPrev;
  pNewDLLElem->pNextDLLElem=pNext;
  return pNewDLLElem;
}

PDLLELEM
DLL_InsertHead(PDLLIST pdll, void *pvData)
{
  PDLLELEM pNewDLLElem;

  /* Create new elemenet */
  pNewDLLElem=CreateElement(pvData, NULL, pdll->pHeadDLLElem);
  if(!pNewDLLElem)
  {
    return NULL;
  }
  
  /* Change old head pointer to prev */
  if(pdll->pHeadDLLElem)
  {
    pdll->pHeadDLLElem->pPrevDLLElem=pNewDLLElem;
  }
  else
  {
    pdll->pTailDLLElem=pNewDLLElem;
  }

  /* Change pointer to head */
  pdll->pHeadDLLElem=pNewDLLElem;
  return pNewDLLElem;
}

PDLLELEM
DLL_InsertAfter(PDLLIST pdll, PDLLELEM pdllElem, void *pvData)
{
  PDLLELEM pNewDLLElem;
  PDLLELEM pPrevElem;
  PDLLELEM pNextElem;

  /* Create new elemenet */
  pPrevElem=pdllElem;
  pNextElem=pdllElem->pNextDLLElem;
  pNewDLLElem=CreateElement(pvData, pPrevElem, pNextElem);
  if(!pNewDLLElem)
  {
    return NULL;
  }

  /* Change next pointer from previous element */
  pPrevElem->pNextDLLElem=pNewDLLElem;

  /* Change prev pointer from next element */
  if(pNextElem)
  {
    pNextElem->pPrevDLLElem=pNewDLLElem;
  }
  else
  {
    pdll->pTailDLLElem=pNewDLLElem;
  }
  return pNewDLLElem;
}

PDLLELEM
DLL_InsertBefore(PDLLIST pdll, PDLLELEM pdllElem, void *pvData)
{
  if(pdllElem->pPrevDLLElem)
  {
    return DLL_InsertAfter(pdll, pdllElem->pPrevDLLElem, pvData);
  }
  else
  {
    return DLL_InsertHead(pdll, pvData);
  }
}

PDLLELEM
DLL_InsertTail(PDLLIST pdll, void *pvData)
{
  if(pdll->pTailDLLElem)
  {
    return DLL_InsertAfter(pdll, pdll->pTailDLLElem, pvData);
  }
  else
  {
    return DLL_InsertHead(pdll, pvData);
  }
}

PDLLELEM
DLL_GetHeadElem(PDLLIST pdll)
{
  return pdll->pHeadDLLElem;
}

void *
DLL_GetNextElem(PDLLELEM *ppElem)
{
  void *pvData=(*ppElem)->pvData;

  *ppElem=(*ppElem)->pNextDLLElem;
  return pvData;
}

PDLLELEM
DLL_GetTailElem(PDLLIST pdll)
{
  return pdll->pTailDLLElem;
}

void *
DLL_GetPrevElem(PDLLELEM *ppElem)
{
  void *pvData=(*ppElem)->pvData;

  *ppElem=(*ppElem)->pPrevDLLElem;
  return pvData;
}

void *
DLL_RemoveHead(PDLLIST pdll)
{
  PDLLELEM pElem=pdll->pHeadDLLElem;

  if(pElem)
  {
    void *pvData=pElem->pvData;

    pdll->pHeadDLLElem=pElem->pNextDLLElem;
    free(pElem);
    pElem=pdll->pHeadDLLElem;
    if(pElem)
    {
      pElem->pPrevDLLElem=NULL;
    }
    return pvData;
  }
  return NULL;
}
